package projectx.core.dao;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;

import org.apache.log4j.Logger;

import projectx.core.base.ProjectXProperties;
import projectx.core.base.ProjectXSystemException;
import projectx.core.base.ThreadContext;
import projectx.core.dao.impl.PreparedStatementBuilder;
import projectx.util.StringUtil;

public class AbstractDAO {

	// --------------------------------------------------------------------------
	// S T A T I C M E M B E R S
	//
	// --------------------------------------------------------------------------
	/** Max Record Count */
	protected static final int REQUEST_MAX_COUNT = ProjectXProperties
			.getPropertyInt("request.max.record", 1000);

	protected static final int arvRequestMaxCount = ProjectXProperties
			.getPropertyInt("arv.request.max.record", 200);

	/** Max Record SQL */
	private static final String MAX_FETCH_BEGIN = " FETCH FIRST ";// +

	private static final String MAX_FETCH_END = " ROWS ONLY";

	private static final String ARV_MAX_FETCH_SQL = " FETCH FIRST "
			+ (arvRequestMaxCount + 1) + " ROWS ONLY";

	/** Logger */
	protected Logger _log = Logger.getLogger(this.getClass());

	protected Connection _conn = null;

	protected String _currentUserId = null;

	protected PreparedStatementBuilder _builder;

	/**
	 * 例外ハンドラーです。
	 */
	protected ExceptionHandler _exHandler = ExceptionHandler.getHandler();

	// --------------------------------------------------------------------------
	// M E T H O D S
	//
	// --------------------------------------------------------------------------
	/**
	 * Consttuctor
	 * 
	 * @throws ProjectXSystemException
	 *             PolarisSystemException
	 */
	public AbstractDAO() throws ProjectXSystemException {
		_conn = ThreadContext.getConnection();
		_currentUserId = (String) ThreadContext.getCurrentAuth();
	}

	public String addpendMaxFetchSQL(String sql) {
		return addpendMaxFetchSQL(sql, REQUEST_MAX_COUNT + 1);
	}

	public String addpendMaxFetchSQL(String sql, int maxrecordCount) {
		if (sql == null) {
			return sql;
		}

		StringBuffer sqlBuffer = new StringBuffer(sql);
		sqlBuffer.append(MAX_FETCH_BEGIN);
		sqlBuffer.append(String.valueOf(maxrecordCount));
		sqlBuffer.append(MAX_FETCH_END);
		return sqlBuffer.toString();
	}

	public String addpendArvMaxFetchSQL(String sql) {
		if (sql == null) {
			return sql;
		}

		StringBuffer sqlBuffer = new StringBuffer(sql);
		sqlBuffer.append(ARV_MAX_FETCH_SQL);
		return sqlBuffer.toString();
	}

	public void debug(String msg) {
		_log.debug(msg);
	}

	public void debug(String msg, Throwable t) {
		_log.debug(msg, t);
	}

	public void logError(Exception e) {
		_log.error(e);
	}

	public void debugSql() {
		try {
			_log.debug("SQL ==" + _builder.getSqlLog());
		} catch (Exception e) {
			_log.error("SQL出力エラーがありました" + e.toString());
		}
	}

	public void errorSql(Exception e) {

		try {
			_log.error("SQL ==" + _builder.getSqlLog(), e);
		} catch (Exception esql) {
			_log.error("SQL Error" + e.toString() + esql.toString());
		}
	}

	public String getString(Object value) {
		if (value == null) {
			return "";
		}
		return StringUtil.trim(value.toString());
	}

	protected byte[] toBytes(Blob value) throws SQLException, IOException {
		if (value == null) {
			return new byte[0];
		}
		BufferedInputStream bis = null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			InputStream is = value.getBinaryStream();
			bis = new BufferedInputStream(is);
			byte[] buf = new byte[1024 * 4];
			baos = new ByteArrayOutputStream();
			int actualLength = 0;
			while ((actualLength = bis.read(buf, 0, buf.length)) > -1) {
				baos.write(buf, 0, actualLength);
			}
		} finally {
			if (null != baos) {
				baos.close();
			}
			if (null != bis) {
				bis.close();
			}
		}
		return baos.toByteArray();
	}

	protected void updateResultSet(ResultSet rs, int index, Object value,
			int sqlType) throws SQLException, IOException {
		if (value == null) {
			rs.updateNull(index);
			return;
		}

		switch (sqlType) {
		case Types.VARCHAR:
			rs.updateString(index, value.toString());
			break;
		case Types.BOOLEAN:
			rs.updateBoolean(index, ((Boolean) value).booleanValue());
			break;
		case Types.TINYINT:
			rs.updateByte(index, ((Byte) value).byteValue());
			break;
		case Types.SMALLINT:
			rs.updateShort(index, ((Short) value).shortValue());
			break;
		case Types.INTEGER:
			rs.updateInt(index, ((Integer) value).intValue());
			break;
		case Types.BIGINT:
			rs.updateLong(index, ((Long) value).longValue());
			break;
		case Types.REAL:
			rs.updateFloat(index, ((Float) value).floatValue());
			break;
		case Types.DOUBLE:
			rs.updateDouble(index, ((Double) value).doubleValue());
			break;
		case Types.DATE:
			rs.updateDate(index, (Date) value);
			break;
		case Types.TIME:
			rs.updateTime(index, (Time) value);
			break;
		case Types.TIMESTAMP:
			rs.updateTimestamp(index, (Timestamp) value);
			break;
		case Types.DECIMAL:
			rs.updateBigDecimal(index, (BigDecimal) value);
			break;
		case Types.BLOB:
			ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) value);
			rs.updateBinaryStream(index, bais, bais.available());
			break;
		case Types.CLOB:
			StringReader sr = new StringReader((String) value);
			rs.updateCharacterStream(index, sr, ((String) value).length());
			break;
		default:
			rs.updateObject(index, value);
			break;
		}
	}

	protected String toString(Clob value) throws SQLException, IOException {
		if (value == null) {
			return null;
		}
		StringBuffer sb = new StringBuffer();
		Reader r = null;
		try {
			r = value.getCharacterStream();
			int i = 0;
			while ((i = r.read()) != -1) {
				sb.append(i);
			}
		} finally {
			if (r != null) {
				r.close();
			}
		}
		return sb.toString();
	}

	public void setExceptionHandler(ExceptionHandler exHandler) {
		_exHandler = exHandler;
	}
}